home *** CD-ROM | disk | FTP | other *** search
- //
- // MiscThreeStateButtonCell.m -- cell class for the three state button
- // Written by Don Yacktman Copyright (c) 1993 by Don Yacktman.
- // Version 1.0. All rights reserved.
- //
- // This notice may not be removed from this source code.
- //
- // This is a free object! Contact the author for the latest version.
- // Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604
- // e-mail: Don_Yacktman@byu.edu
- //
- // This object is included in the MiscKit by permission from the author
- // and its use is governed by the MiscKit license, found in the file
- // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
-
- #import "MiscThreeStateButtonCell.h"
- #import "MiscThreeStateButton.h"
-
- // This would be a hell of a lot easier with NeXT's source code. Since
- // I don't have it, but I want to make sure that the same drawing mechanisms
- // are used to render the third state of the button, I do a nasty hack.
- // Whenever I want to go to the third state, I fake it and make the button
- // think it's in the alternate state (of a normal button) but whenever it
- // attempts to get the title or image, I swap in the third state button/image
- // before the render and then switch back the true alt image/title upon
- // finishing the render. I use the same trick when the button is being queried
- // about it's sizes for drawing areas, etc. I swap icons and titles in and get
- // the size they'd use, then I get the size of the correct icons, and then
- // return the MAX of the two. Without knowing exactly how NeXT does the
- // calculations, I have to do a hack like this. Since I'm essentially re-using
- // their code when I take my measurements and do my rendering, that means that
- // if it changes in the future, this class will follow the changes and keep
- // doing the right thing, whatever that is. I still think it's ugly, but it is
- // a cute trick.
-
- // As usual, ***** denotes an unfinished method. This class is not at all
- // done. It won't do what it is supposed to do yet, at all!
-
- // Allowing NX_CONTENTS ("show alternate icon/title") as a highlighting mode
- // just creates a ton of problems, so it needs to be disallowed. Below is the
- // set of allowable highlight modes to be ANDed with whatever highlight mode
- // is sent in, which should remove NX_CONTENTS as a possibility. -Carl
- #define ALLOWABLE_HIGHLIGHTS (NX_NONE|NX_PUSHIN|NX_CHANGEGRAY|NX_CHANGEBACKGROUND)
-
- @implementation MiscThreeStateButtonCell
-
- - init
- {
- id ret = [super init];
- thirdTitle = NULL;
- _thirdImage = nil;
- isCyclic = YES;
- thirdState = NO;
-
- altClicked = NO; // Set these hacks correctly :-) -Carl
- dontIncrement = NO;
-
- // Disallow NX_CONTENTS as highlight mode -Carl
- [super setHighlightsBy:([self highlightsBy] & ALLOWABLE_HIGHLIGHTS)];
- return ret;
- }
-
- - copyFromZone:(NXZone *)zone
- {
- id newClass = [super copyFromZone:zone];
- // copy these two strings so they won't get freed twice or more...
- thirdTitle =
- NXCopyStringBufferFromZone([newClass thirdTitle], [self zone]);
- trueAltTitle =
- NXCopyStringBufferFromZone([newClass altTitle], [self zone]);
- return newClass;
- }
-
- - free
- {
- if (thirdTitle) free(thirdTitle);
- // [_thirdImage free]; // should I do this?
- return [super free];
- }
-
- - (const char *)altTitle { return trueAltTitle; }
-
- - setAltTitle:(const char *)aString
- {
- if (trueAltTitle) free(trueAltTitle);
- trueAltTitle = NXCopyStringBufferFromZone(aString, [self zone]);
- return self;
- }
-
- - (const char *)altIcon { return [_trueAltImage name]; }
-
- - setAltIcon:(const char *)iconName
- {
- return [self setAltImage:[NXImage findImageNamed:iconName]];
- }
-
- - altImage { return _trueAltImage; }
-
- - setAltImage:image
- {
- // [_trueAltImage free]; // should I do this?
- _trueAltImage = image;
- return self;
- }
-
- - (const char *)thirdTitle { return thirdTitle; }
-
- - setThirdTitle:(const char *)aString
- {
- if (thirdTitle) free(thirdTitle);
- thirdTitle = NXCopyStringBufferFromZone(aString, [self zone]);
- return self;
- }
-
- - (const char *)thirdIcon { return [_thirdImage name]; }
-
- - setThirdIcon:(const char *)iconName
- {
- return [self setThirdImage:[NXImage findImageNamed:iconName]];
- }
-
- - thirdImage { return _thirdImage; }
-
- - setThirdImage:image
- {
- // [_thirdImage free]; // should I do this?
- _thirdImage = image;
- return self;
- }
-
- - setType:(int)aType
- {
- switch (aType) {
- case MISC_CYCLIC_THREE_STATE: isCyclic = YES; break;
- case MISC_PLAIN_THREE_STATE: isCyclic = NO; break;
- default: return [super setType:aType];
- }
- return self;
- }
-
- - (BOOL)isCyclic { return isCyclic; }
-
- - (const char *)stringValue
- {
- if (thirdState) return "alt";
- return [super stringValue];
- }
-
- - setStringValue:(const char *)aString
- {
- thirdState = NO;
- if (aString) if (strlen(aString)) thirdState = YES;
- return [super setStringValue:aString];
- }
-
- - setStringValueNoCopy:(const char *)aString
- {
- thirdState = NO;
- if (aString) if (strlen(aString)) thirdState = YES;
- return [super setStringValueNoCopy:aString];
- }
-
- - (int)intValue
- {
- if (thirdState) return 2;
- return [super intValue];
- }
-
- - setIntValue:(int)anInt
- {
- if (anInt == 2) thirdState = YES;
- else thirdState = NO;
- return [super setIntValue:anInt];
- }
-
- - (float)floatValue
- {
- if (thirdState) return 2.0;
- return [super floatValue];
- }
-
- - setFloatValue:(float)aFloat
- {
- if ((aFloat > 1.0) && (aFloat <= 2.0)) thirdState = YES;
- else thirdState = NO;
- return [super setFloatValue:aFloat];
- }
-
- - (double)doubleValue
- {
- if (thirdState) return 2.0;
- return [super doubleValue];
- }
-
- - setDoubleValue:(double)aDouble
- {
- if ((aDouble > 1.0) && (aDouble <= 2.0)) thirdState = YES;
- else thirdState = NO;
- return [super setDoubleValue:aDouble];
- }
-
- // Here's where the fun begins with us intercepting and fooling the button.
- - getDrawRect:(NXRect *)theRect
- { // perform the super method for both alt and third state, then merge rects
- // Note, this may not really be necessary, since the NeXT implementation
- // may use the two methods below to give the answer to this method. If
- // that's the case, then this method doesn't need the override, since the
- // two methods below take the third state into account. If they don't we
- // need this method. The docs don't say one way or the other, and I don't
- // feel like disassembling the Appkit, so at worst, we'll be inefficient
- // here. This is a good reason for why NeXT should let us look at source,
- // or make MAJOR improvements in the docs!
- NXRect tempRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- [super getDrawRect:&tempRect];
- [self replaceAltTitle:trueAltTitle];
- icon.bmap.alternate = _trueAltImage;
- [super getDrawRect:theRect];
- if (thirdState) {
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- }
- NXUnionRect(&tempRect, theRect);
- return self;
- }
-
- - getTitleRect:(NXRect *)theRect
- { // perform the super method for both alt and third state, then merge rects
- NXRect tempRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- [super getTitleRect:&tempRect];
- [self replaceAltTitle:trueAltTitle];
- icon.bmap.alternate = _trueAltImage;
- [super getTitleRect:theRect];
- if (thirdState) {
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- }
- NXUnionRect(&tempRect, theRect);
- return self;
- }
-
- - getIconRect:(NXRect *)theRect
- { // perform the super method for both alt and third state, then merge rects
- NXRect tempRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- [super getIconRect:&tempRect];
- [self replaceAltTitle:trueAltTitle];
- icon.bmap.alternate = _trueAltImage;
- [super getIconRect:theRect];
- if (thirdState) {
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- }
- NXUnionRect(&tempRect, theRect);
- return self;
- }
-
-
- - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
- {
- NXSize tempSize = { 0.0, 0.0 };
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- [super calcCellSize:&tempSize inRect:aRect];
- [self replaceAltTitle:trueAltTitle];
- icon.bmap.alternate = _trueAltImage;
- [super calcCellSize:theSize inRect:aRect];
- if (thirdState) {
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- }
- theSize->width = MAX(theSize->width, tempSize.width);
- theSize->height = MAX(theSize->height, tempSize.height);
- return self;
- }
-
- - drawInside:(const NXRect *)aRect inView:controlView
- {
- if (thirdState) {
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- } else {
- [self replaceAltTitle:trueAltTitle];
- icon.bmap.alternate = _trueAltImage;
- }
- return [super drawInside:aRect inView:controlView];
- }
-
- - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
- {
- if (thirdState) {
- icon.bmap.alternate = _thirdImage;
- [self replaceAltTitle:thirdTitle];
- } else {
- [self replaceAltTitle:trueAltTitle];
- icon.bmap.alternate = _trueAltImage;
- }
- return [super highlight:cellFrame inView:controlView lit:flag];
- }
-
- #define ALTKEY (theEvent->flags & NX_ALTERNATEMASK)
-
- - (BOOL)trackMouse:(NXEvent *)theEvent inRect:(const NXRect *)cellFrame
- ofView:controlView
- {
- altClicked = (ALTKEY)? YES:NO;
- return [super trackMouse:theEvent inRect:cellFrame ofView:controlView];
- }
-
- - replaceTitle:(const char *)aString
- {
- if (contents) free(contents);
- contents = NXCopyStringBufferFromZone(aString, [self zone]);
- return self;
- }
-
- - replaceAltTitle:(const char *)aString
- {
- if (altContents) free(altContents);
- altContents = NXCopyStringBufferFromZone(aString, [self zone]);
- return self;
- }
-
- - performClick:sender
- {
- return [super performClick:sender];
- }
-
- - performAltClick:sender
- {
- altClicked = YES;
-
- // This is a total hack... For some reason, whenever -performClick
- // or -performAltClick was called and the current state was 0,
- // -incrementState was called *twice*. This did not happen when using
- // the mouse, nor did it happen when state was 1 or 2.
- // This did not seem to affect -performClick -- I have no idea why --
- // but this was a problem with -performAltClick. Therefore, I ask
- // -incrementState to not execute on the first call to it after this
- // method by way of the special case ivar, 'dontIncrement'. -Carl
- if ([self intValue] == 0) dontIncrement = YES;
-
- return [super performClick:sender];
- }
-
- - write:(NXTypedStream *)stream
- {
- id ret = [super write:stream];
- NXWriteTypes(stream, "**cc", &thirdTitle, &trueAltTitle,
- &isCyclic, &thirdState);
- NXWriteObject(stream, _thirdImage);
- NXWriteObject(stream, _trueAltImage);
- return ret;
- // altClicked and dontIncrement don't really need to be
- // written... they are always NO at this point. See -read.
- }
-
- - read:(NXTypedStream *)stream
- {
- id ret = [super read:stream];
- NXReadTypes(stream, "**cc", &thirdTitle, &trueAltTitle,
- &isCyclic, &thirdState);
- _thirdImage = NXReadObject(stream);
- _trueAltImage = NXReadObject(stream);
-
- // Disallow NX_CONTENTS as highlight mode -Carl
- [super setHighlightsBy:([self highlightsBy] & ALLOWABLE_HIGHLIGHTS)];
-
- // The following two ivars are used to inform -incrementState
- // of a couple things. They are only YES for very brief periods
- // of time, so they really don't need to be archived in -write.
- // Someone would have to be rather diabolical to manage to have
- // -write called when they were YES... :-) -Carl
- altClicked = NO;
- dontIncrement = NO;
- return ret;
- }
-
- - incrementState
- {
- int next = 0;
- int curState = [self intValue];
-
- if (dontIncrement) { //see note in -performAltClick
- dontIncrement = NO;
- return self;
- }
- switch (curState) {
- case 0 : next = 1; break;
- case 1 : if (isCyclic) next = 2;
- else next = 0;
- break;
- case 2 : next = 0; break;
- default: break;
- }
- if (altClicked && (curState < 2)) next = 2;
- [self setIntValue:next]; //disable flushWindow around this??
- altClicked = NO;
- return self;
- }
-
- - setHighlightsBy:(int)aType;
- {
- // Disallow NX_CONTENTS as highlight mode -Carl
- return [super setHighlightsBy:(aType & ALLOWABLE_HIGHLIGHTS)];
- }
-
- @end
-